package com.tencent.wxcloudrun.service.business;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.tencent.wxcloudrun.config.CustomException;
import com.tencent.wxcloudrun.pojo.dto.resp.MapCloudMainResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

/**
 * 腾讯位置服务，地点云  <a href="https://lbs.qq.com/service/placeCloud/placeCloudGuide/cloudOverview">...</a>
 */
@Service
@Slf4j
public class MapCloudService {

    /**
     * 表（结构）查询接口
     */
    private static final String TABLE_LIST = "table/list";

    /**
     * 创建数据（create）接口  通过本接口，可向地点云指定数据表存入一条或多条数据
     */
    private static final String DATA_CREATE = "data/create";

    /**
     * 修改数据（update）接口  通过本接口，可对指定单条数据进行修改，也可对一组id或ud_id的数据统一修改指定字段值。
     */
    private static final String DATA_UPDATE = "data/update";

    /**
     * 删除（delete）接口  通过本接口，可一次删除多条(一组id或ud_id的)数据，使用时请谨慎。
     */
    private static final String DATA_DELETE = "data/delete";

    /**
     * 数据列表（list）接口  本接口访问地点云存储中的数据，通过翻页可遍历全表数据，可用于显示数据列表 或 数据导出。 <br>
     * 注：与地点云搜索不同，地点云搜索因生成索引需要一定耗时，刚刚存入的数据一般不会马上可搜索到，而本接口直接访问云存储内容，存入数据即时可见，但本接口不提供搜索与筛选条件。
     */
    private static final String DATA_LIST = "data/list";

    // `````````````````````````````````````https://lbs.qq.com/service/placeCloud/placeCloudGuide/cloudDataManage

    // `````````````````````````````````````https://lbs.qq.com/service/placeCloud/placeCloudGuide/cloudSearch

    /**
     * 周边搜索（圆形范围）
     */
    private static final String SEARCH_NEARBY = "search/nearby";

    /**
     * 区域搜索
     */
    private static final String SEARCH_REGION = "search/region";

    /**
     * 矩形范围搜索
     */
    private static final String SEARCH_RECTANGLE = "search/rectangle";

    /**
     * 围栏搜索  围栏搜索作用于地点云数据中的Polygon（轮廓范围）字段，通过一个坐标点，搜索其所在地点的面状范围。 <br>
     * 可用于场景诸如：精准查找附近支持上门服务覆盖的门店，可用于判断某坐标是否在园区内，也可用于终端、运动状态的设备判断触发电子围栏事件等场景。
     */
    private static final String SEARCH_CONTAIN = "search/contain";

    private static final String PLACE_CLOUD = "/place_cloud/";

    private static final String WS_GEOCODER_V1 = "/ws/geocoder/v1";

    @Value("${mapCloud.key}")
    private String mapCloudKey;

    @Value("${mapCloud.url}")
    private String mapCloudUrl;

    @Value("${mapCloud.sk}")
    private String mapCloudSk;

    /**
     * 本接口提供由经纬度到文字地址及相关位置信息的转换能力
     */
    public JSONObject geoCoder(double lat, double lng) {
        if (ObjUtil.isNull(lat) || ObjUtil.isNull(lng)) {
            throw new CustomException("逆地址解析能力的调用必须传经纬度值。");
        }
        Map<String, Object> paramMap = new LinkedHashMap<>();
        paramMap.put("key", mapCloudKey);
        paramMap.put("location", lat + "," + lng);
        // 生成签名
        String sigOri = WS_GEOCODER_V1 + "?key=" + mapCloudKey + "&location=" + lat + "," + lng + mapCloudSk;
        String sig = MD5.create().digestHex(sigOri);
        paramMap.put("sig", sig);
        String rltStr = HttpRequest.get(mapCloudUrl + WS_GEOCODER_V1).form(paramMap).execute().body();
        MapCloudMainResp mainResp = JSONObject.parseObject(rltStr, MapCloudMainResp.class);
        MapCloudMainResp.rltBasicCheck(mainResp);
        return mainResp.getResult();
    }

    /**
     * 从json中提取市 <br>
     * <a href="https://lbs.qq.com/service/webService/webServiceGuide/address/Gcoder"/>
     */
    public String getCityFromJSON(JSONObject jsonObject) {
        return jsonObject.getJSONObject("ad_info").getString("city");
    }

    /**
     * 获取表信息，不传参就获取全量表数据。
     */
    public JSONObject tableList() {
        // 生成签名
        String sig = "key=" + mapCloudKey;
        Map<String, Object> paramMap = new LinkedHashMap<>();
        paramMap.put("key", mapCloudKey);
        sig = MD5.create().digestHex(PLACE_CLOUD + TABLE_LIST + "?" + sig + mapCloudSk);
        paramMap.put("sig", sig);
        String rltStr = HttpRequest.get(mapCloudUrl + PLACE_CLOUD + TABLE_LIST).form(paramMap).execute().body();
        MapCloudMainResp mainResp = JSONObject.parseObject(rltStr, MapCloudMainResp.class);
        MapCloudMainResp.rltBasicCheck(mainResp);
        return mainResp.getResult();
    }

    /**
     * 本接口访问地点云存储中的数据，通过翻页可遍历全表数据，可用于显示数据列表 或 数据导出。<br>
     * 注：与地点云搜索不同，地点云搜索因生成索引需要一定耗时，刚刚存入的数据一般不会马上可搜索到，而本接口直接访问云存储内容，存入数据即时可见，但本接口不提供搜索与筛选条件。
     */
    public JSONObject dataList(String tableId) {
        String sigOri = PLACE_CLOUD + DATA_LIST + "?key=" + mapCloudKey + "&orderby=id desc&page_index=1&page_size=200" + "&table_id=" + tableId + mapCloudSk;
        String sig = MD5.create().digestHex(sigOri);
        Map<String, Object> paramMap = new LinkedHashMap<>();
        paramMap.put("key", mapCloudKey);
        paramMap.put("orderby", "id desc");
        paramMap.put("page_index", "1");
        paramMap.put("page_size", "200");
        paramMap.put("table_id", tableId);
        paramMap.put("sig", sig);
        String rltStr = HttpRequest.get(mapCloudUrl + PLACE_CLOUD + DATA_LIST).form(paramMap).execute().body();
        MapCloudMainResp mainResp = JSONObject.parseObject(rltStr, MapCloudMainResp.class);
        MapCloudMainResp.rltBasicCheck(mainResp);
        return mainResp.getResult();
    }

    public List<JSONObject> getPointFromJSON(JSONObject jsonObject) {
        JSONArray jsonArray = jsonObject.getJSONArray("data");
        List<JSONObject> resp = new ArrayList<>();
        if (CollUtil.isEmpty(jsonArray)) {
            return resp;
        }
        for (Object oo : jsonArray) {
            JSONObject hashMap = new JSONObject();
            JSONObject parsedObject = JSON.parseObject(JSONObject.toJSONString(oo));
            hashMap.put("udId", parsedObject.getString("ud_id"));
            JSONObject location = parsedObject.getJSONObject("location");
            hashMap.put("lat", location.getDouble("lat"));
            hashMap.put("lng", location.getDouble("lng"));
            JSONObject x = parsedObject.getJSONObject("x");
            hashMap.put("price", x.getBigDecimal("price"));
            resp.add(hashMap);
        }
        return resp;
    }

    /**
     * 周边搜索（圆形范围）<br>
     * <a href="https://lbs.qq.com/service/placeCloud/placeCloudGuide/cloudSearch#1"/>
     *
     * @param tableId  表id，即表的唯一标识（table_id在表创建后由系统自动生成
     * @param location 搜索中心点坐标	location=39.976194,116.317987
     * @param radius   半径，单位：米，默认5,000米，最大支持20,000米，参数值超过 20,000米时，只返回20,000米内的数据
     * @param down     可为null   最低价
     * @param up       可为null   最高价
     */
    public JSONObject searchNearby(String tableId, String location, String radius, String down, String up) {
        String sigOri = PLACE_CLOUD + SEARCH_NEARBY;
        String price = null;
        if (StrUtil.isNotBlank(down) && StrUtil.isBlank(up)) { // 只有最低价时
            price = "x.price>" + down;
        }
        if (StrUtil.isNotBlank(up) && StrUtil.isBlank(down)) { // 只有最高价时
            price = "x.price<" + up;
        }
        if (StrUtil.isNotBlank(up) && StrUtil.isNotBlank(down)) { // 两个都有时
            price = "x.price>" + down + " and x.price<" + up;
        }
        if (StrUtil.isNotBlank(price)) {
            sigOri = sigOri + "?filter=" + price + "&key=" + mapCloudKey + "&location=" + location + "&orderby=" + "distance(" + location + ")" +
                    "&page_index=1&page_size=200&radius=" + radius + "&table_id=" + tableId + mapCloudSk;
        } else {
            sigOri = sigOri + "?key=" + mapCloudKey + "&location=" + location + "&orderby=" + "distance(" + location + ")" +
                    "&page_index=1&page_size=200&radius=" + radius + "&table_id=" + tableId + mapCloudSk;
        }
        String sig = MD5.create().digestHex(sigOri);
        Map<String, Object> paramMap = new LinkedHashMap<>();
        if (StrUtil.isNotBlank(price)) {
            paramMap.put("filter", price);
        }
        paramMap.put("key", mapCloudKey);
        paramMap.put("location", location);
        paramMap.put("orderby", "distance(" + location + ")");
        paramMap.put("page_index", "1");
        paramMap.put("page_size", "200");
        paramMap.put("radius", radius);
        paramMap.put("table_id", tableId);
        paramMap.put("sig", sig);
        String rltStr = HttpRequest.get(mapCloudUrl + PLACE_CLOUD + SEARCH_NEARBY).form(paramMap).execute().body();
        MapCloudMainResp mainResp = JSONObject.parseObject(rltStr, MapCloudMainResp.class);
        MapCloudMainResp.rltBasicCheck(mainResp);
        return mainResp.getResult();
    }

    /**
     * 单条新增点坐标信息
     */
    public JSONObject dataCreate(JSONObject jsonObject) {
        String udId = jsonObject.getString("ud_id"); // 自定义id
        String title = jsonObject.getString("title"); // 点名称
        Double lat = jsonObject.getDouble("lat"); // 纬度
        Double lng = jsonObject.getDouble("lng"); // 经度
        String tableId = jsonObject.getString("table_id"); // 表id，即表的唯一标识
        BigDecimal price = jsonObject.getBigDecimal("price"); // 价格
        if (StrUtil.isBlank(udId) || StrUtil.isBlank(title) || ObjUtil.isNull(lat) || ObjUtil.isNull(lng) || StrUtil.isBlank(tableId)) {
            throw new CustomException("新增点坐标时缺少必填参数！");
        }
        HashMap<String, Object> req = new LinkedHashMap<>();
        req.put("key", mapCloudKey);
        req.put("table_id", tableId);
        JSONObject dataObj = new JSONObject();
        dataObj.put("ud_id", udId);
        dataObj.put("title", title);
        JSONObject location = new JSONObject();
        location.put("lat", lat);
        location.put("lng", lng);
        dataObj.put("location", location);
        JSONObject x = new JSONObject();
        x.put("price", price);
        dataObj.put("x", x);
        ArrayList<JSONObject> arrayList = new ArrayList<>();
        arrayList.add(dataObj);
        req.put("data", arrayList);
        String sigOri = PLACE_CLOUD + DATA_CREATE + "?data=" + JSON.toJSONString(arrayList) + "&key=" + mapCloudKey + "&table_id=" + tableId + mapCloudSk;
        String sig = MD5.create().digestHex(sigOri);
        String rltStr = HttpRequest.post(mapCloudUrl + PLACE_CLOUD + DATA_CREATE + "?sig=" + sig).body(JSON.toJSONString(req)).execute().body();
        MapCloudMainResp mainResp = JSONObject.parseObject(rltStr, MapCloudMainResp.class);
        MapCloudMainResp.rltBasicCheck(mainResp);
        JSONArray jsonArray = mainResp.getResult().getJSONArray("failure");
        if (CollUtil.isNotEmpty(jsonArray)) {
            log.error("地图云新增地点出现异常  {}", JSONObject.toJSONString(jsonArray));
            throw new CustomException("地图云新增地点出现异常。");
        }
        return mainResp.getResult();
    }

    /**
     * 只能改单条价格
     */
    public JSONObject dataUpdate(JSONObject jsonObject) {
        String udId = jsonObject.getString("ud_id"); // 自定义id
        String tableId = jsonObject.getString("table_id"); // 表id，即表的唯一标识
        BigDecimal price = jsonObject.getBigDecimal("price"); // 价格，只能改价格
        if (StrUtil.isBlank(udId) || ObjUtil.isNull(price) || StrUtil.isBlank(tableId)) {
            throw new CustomException("修改点坐标时缺少必填参数！");
        }
        HashMap<String, Object> req = new HashMap<>();
        req.put("key", mapCloudKey);
        req.put("table_id", tableId);
        JSONObject dataObj = new JSONObject();
        JSONObject x = new JSONObject();
        x.put("price", price);
        dataObj.put("x", x);
        req.put("data", dataObj);
        req.put("filter", "ud_id in(\"" + udId + "\")");
        String sigOri = PLACE_CLOUD + DATA_UPDATE + "?data=" + JSON.toJSONString(dataObj) + "&filter=" + "ud_id in(\"" + udId + "\")" + "&key=" + mapCloudKey + "&table_id=" + tableId + mapCloudSk;
        String sig = MD5.create().digestHex(sigOri);
        String rltStr = HttpRequest.post(mapCloudUrl + PLACE_CLOUD + DATA_UPDATE + "?sig=" + sig).body(JSON.toJSONString(req)).execute().body();
        MapCloudMainResp mainResp = JSONObject.parseObject(rltStr, MapCloudMainResp.class);
        MapCloudMainResp.rltBasicCheck(mainResp);
        JSONArray jsonArray = mainResp.getResult().getJSONArray("failure");
        if (CollUtil.isNotEmpty(jsonArray)) {
            log.error("地图云修改地点出现异常  {}", JSONObject.toJSONString(jsonArray));
            throw new CustomException("地图云修改地点出现异常。");
        }
        return mainResp.getResult();
    }
}
